#include "tests.h"
#include "email.h"
#include "utilities.h"
#include "test_images.h"
#include "date.h"
#include <curl/curl.h>
#include <windows.h> 

//#define DEBUG

enum
{
    TIME_TO_WAIT_FOR_EMAIL_TO_BE_RECEIVED_BY_LOCAL_SERVER_MS = 100,
    NUM_TEST_ATTACHMENTS = 3
};

static char * last_email_subject = 0;

/* keep the files listed in the following small or sending the test email will be slow */
static char * test_attachments[NUM_TEST_ATTACHMENTS + 1];

struct MemoryStruct {
  char *memory;
  size_t size;
};

static char header_field_value[HEADER_FIELD_BUFFER_LENGTH];
static char big_buffer[EMAIL_BUFFER_LENGTH];

/*
    When there are no attachments, the body consists of the data following 
    the Messag-ID field. It is marked with BODY_TEXT in the following.

Message-ID: <fcetfrycvdzfhz@agilerss.org>

BODY_TEXT
*/
char * extract_body_string_from_buffer_not_containing_attachments(char * buffer)
{
    const char * content_type_prefix = "Content-Type: text/";
    char * ptr = strstr(buffer, content_type_prefix);
    
    if (ptr)
    {
        ptr += strlen(content_type_prefix);

        /* check which one of "plain" and "html" follows */
        if (*ptr == 'p')
        {
            ptr += strlen("plain\r\n\r\n");
        }
        else if (*ptr == 'h')
        {
            ptr += strlen("html\r\n\r\n");
        }

        /* ignore the extra \r\n at the end */
        memcpy(big_buffer, ptr, strlen(ptr) - 2);

        /* turn it into a string */
        big_buffer[strlen(ptr) - 2] = '\0';
    }
    else
    {
        big_buffer[0] = '\0';
    }

    return big_buffer;
}

/*
    When there are attachments, you will see a block similar to the
    following around the body text. The actual body text is marked with BODY_TEXT.

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=SEPARATOR=_1665_5627_5976_0653_1122_7914_="

--=SEPARATOR=_1665_5627_5976_0653_1122_7914_=
Content-Type: text/plain

BODY_TEXT
--=SEPARATOR=_1665_5627_5976_0653_1122_7914_=
*/
char * extract_body_string_from_buffer_containing_attachments(char * buffer)
{
    char * ptr_start = 0;
    char * ptr_end = 0;
    
    /* first locate "text/plain" */
    ptr_start = strstr(buffer, "text/plain");

    if (ptr_start)
    {
        /* advance to the beginning of the body text */
        ptr_start += strlen("text/plain") + 2 + 2;

        /* find where the subject ends */
        ptr_end = strstr(ptr_start, "\r\n--=SEPARATOR=_");

        memcpy(big_buffer, ptr_start, ptr_end - ptr_start);
    }
    
    /* turn it into a string */
    big_buffer[ptr_end - ptr_start] = '\0';

    return big_buffer;
}

char * extract_header_field_value_string_from_buffer(char * buffer, char * field)
{
    int i = 0;
    char * ptr = strstr(buffer, field);
    
    if (ptr)
    {
        /* advance the pointer to the beginning of the Message-ID */
        ptr += strlen(field);

        /* copy the characters until reaching a delimiter */
        while (*ptr != '\r' && *ptr != '\n')
        {
            header_field_value[i] = *ptr;
            ++ptr;
            ++i;
        }
    }

    header_field_value[i] = '\0';

    return header_field_value;
}

/*
    For an attachement you will see a block similar to the following.

Content-Type: application/octet-stream; Name="1.png"
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAB7CAAAewgFu0HU+
AAAAQklEQVR42mP4z8BANiIgvYmBgYE8zRcYoIBkzXCdJGuGuNaXDM0QPRkMDC/J0AwEe5HY
ZAbYqOZRzYNbMwGjKdEMAJsFZlpdej3zAAAAAElFTkSuQmCC

--=SEPARATOR=_8793_9460_8099_8147_1728_6107_=
*/
char * extract_attachment_contents_base64_encoded(char * buffer, char * attachment_name)
{
    char * ptr_file_name = 0;
    char * ptr_start = 0;
    char * ptr_end = 0;

    /* first locate the attachment name */
    ptr_file_name = strrchr(attachment_name, '\\') + 1; /* +1 to skip the backslash */
    ptr_start = strstr(buffer, ptr_file_name);

    if (ptr_start)
    {
        /* advance to the beginning of base64 encoded data */
        ptr_start += strlen(ptr_file_name) + strlen("\"\r\nContent-Transfer-Encoding: base64\r\n\r\n");

        /* find where the data ends */
        ptr_end = strstr(ptr_start, "\r\n\r\n--=SEPARATOR=_");

        memcpy(big_buffer, ptr_start, ptr_end - ptr_start);
    }

    /* turn it into a string */
    big_buffer[ptr_end - ptr_start] = '\0';

    return big_buffer;
}

char * get_last_email_subject()
{
    return last_email_subject;
}

void set_last_email_subject(char * subject)
{
    /* previously pointed memory could be dynamically allocated;
       release it before overwriting */
    free(last_email_subject);

    last_email_subject = subject;
}

int init_test_suite_email(void)
{
    int i;

    // use the default ini path!
    if (INI_Init(0) == false)
    {
        return CUE_SINIT_FAILED;
    }
    
    /* populate attachment list */
    for (i = 0; i < NUM_TEST_ATTACHMENTS; ++i)
    {
        test_attachments[i] = (char *)malloc(MAX_PATH_LENGTH);
        if (test_attachments[i])
        {
            _snprintf(test_attachments[i], MAX_PATH_LENGTH - 1, "%s\\%d.png", INI_GetDataPath(), i + 1);
        }
    }

    /* indicate the end of the attachment list */
    test_attachments[i] = (char)NULL;

    return 0;
}

int cleanup_test_suite_email(void)
{
    int i;

    for (i = 0; i < NUM_TEST_ATTACHMENTS; ++i)
    {
        free(test_attachments[i]);
    }

    set_last_email_subject(0);

    INI_Free();

    return 0;
}

void test_email_send(bool with_attachments)
{
    bool res;
    char * message_id;
    char * subject;

    message_id = EMAIL_GenerateRandomMessageId();
    subject = DATE_GetSystemDateTime();
    set_last_email_subject(subject);

    res = EMAIL_Send(SENDER_ADDRESS_TEST, RECIPIENT_ADDRESS_TEST, subject,
        message_id, message_id, with_attachments ? test_attachments : NULL, format_opt_text,
        SMTPSERVER_TEST, SMTPPORT_TEST, SENDER_USERNAME_TEST, SENDER_PASSWORD_TEST);

    // wait a litte to let the server receive the message
    Sleep(TIME_TO_WAIT_FOR_EMAIL_TO_BE_RECEIVED_BY_LOCAL_SERVER_MS);

    CU_ASSERT_EQUAL(res, true);
}

void test_email_send_without_attachments()
{
    test_email_send(false);
}

void test_email_send_with_attachments()
{
    test_email_send(true);
}

/* the following code is based on curl-7.27.0\docs\examples\pop3s.c and getinmemory.c */
void email_receive_and_compare(bool attachments_expected)
{
	struct MemoryStruct test_chunk;

	if(EMAIL_Receive(&test_chunk, POP_MAILBOX_URL_TEST, RECIPIENT_USERNAME_TEST,
        RECIPIENT_PASSWORD_TEST))
	{
    /* compare the message id of the received message to make sure that
        it is indeed the previously sent one */
        printf("Received Message-ID '%s'\n", 
        extract_header_field_value_string_from_buffer(test_chunk.memory, EMAIL_HEADER_PREFIX_MESSAGE_ID));
    CU_ASSERT_STRING_EQUAL(extract_header_field_value_string_from_buffer(test_chunk.memory, EMAIL_HEADER_PREFIX_MESSAGE_ID),
        EMAIL_GetLastGeneratedMessageId());

        /* compare subject, body and attachment contents */
#ifdef DEBUG
        printf("Received subject '%s', last sent subject '%s'\n",
            extract_header_field_value_string_from_buffer(test_chunk.memory, EMAIL_HEADER_PREFIX_SUBJECT),
            get_last_email_subject());
#endif
        CU_ASSERT_STRING_EQUAL(extract_header_field_value_string_from_buffer(test_chunk.memory, EMAIL_HEADER_PREFIX_SUBJECT),
            get_last_email_subject());

        if (attachments_expected)
        {
#ifdef DEBUG
        printf("Received body '%s', last sent body '%s'\n",
            extract_body_string_from_buffer_containing_attachments(test_chunk.memory),
            EMAIL_GetLastGeneratedMessageId());
#endif

            /* compare body */
            CU_ASSERT_STRING_EQUAL(extract_body_string_from_buffer_containing_attachments(test_chunk.memory),
                EMAIL_GetLastGeneratedMessageId()); // we used the Message-ID as body

            /* check attachment contents */
            CU_ASSERT_STRING_EQUAL(extract_attachment_contents_base64_encoded(test_chunk.memory, test_attachments[0]),
                                    attachments_base64_encodeded[0]);
            CU_ASSERT_STRING_EQUAL(extract_attachment_contents_base64_encoded(test_chunk.memory, test_attachments[1]),
                                    attachments_base64_encodeded[1]);
            CU_ASSERT_STRING_EQUAL(extract_attachment_contents_base64_encoded(test_chunk.memory, test_attachments[2]),
                                    attachments_base64_encodeded[2]);
        }
        else
        {
#ifdef DEBUG
            printf("Received body '%s', last sent body '%s'\n",
                extract_body_string_from_buffer_not_containing_attachments(test_chunk.memory),
                EMAIL_GetLastGeneratedMessageId());
#endif

            /* compare body */
            CU_ASSERT_STRING_EQUAL(extract_body_string_from_buffer_not_containing_attachments(test_chunk.memory),
                EMAIL_GetLastGeneratedMessageId()); // we used the Message-ID as body
        }

        free(test_chunk.memory);        
	}
}

void test_email_receive_and_compare_without_attachments()
{
    email_receive_and_compare(false);
}

void test_email_receive_and_compare_with_attachments()
{
    email_receive_and_compare(true);
}

void test_send_statistics_0_customers_null_customer()
{
    CU_ASSERT_FALSE(EMAIL_SendStatistics(0, NULL));
}

void test_send_statistics_0_customers_valid_customer()
{
    customer_t customer;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(0, &customer));
}

void test_send_statistics_1_customer_valid_email_text()
{
    customer_t customer;

    customer.email = RECIPIENT_ADDRESS_TEST;
    customer.format_opt = format_opt_text;

    CU_ASSERT_TRUE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_valid_email_html()
{
    customer_t customer;

    customer.email = RECIPIENT_ADDRESS_TEST;
    customer.format_opt = format_opt_html;

    CU_ASSERT_TRUE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_valid_email_invalid_format()
{
    customer_t customer;

    customer.email = RECIPIENT_ADDRESS_TEST;
    customer.format_opt = (format_opt_t)UINT_MAX;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_null_email_text()
{
    customer_t customer;

    customer.email = NULL;
    customer.format_opt = format_opt_text;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_null_email_html()
{
    customer_t customer;

    customer.email = NULL;
    customer.format_opt = format_opt_text;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_null_email_invalid_format()
{
    customer_t customer;

    customer.email = NULL;
    customer.format_opt = (format_opt_t)UINT_MAX;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_invalid_email_text()
{
    customer_t customer;

    customer.email = "1234567890";
    customer.format_opt = format_opt_text;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_invalid_email_html()
{
    customer_t customer;

    customer.email = "@nonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomain.com";
    customer.format_opt = format_opt_html;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_1_customer_invalid_email_invalid_format()
{
    customer_t customer;

    customer.email = "1234567890@nonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomain.com";
    customer.format_opt = (format_opt_t)UINT_MAX;

    CU_ASSERT_FALSE(EMAIL_SendStatistics(1, &customer));
}

void test_send_statistics_2_customers_1st_valid_2nd_valid_email_text()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = RECIPIENT_ADDRESS_TEST;
    customers[1].format_opt = format_opt_text;
    
    CU_ASSERT_TRUE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_valid_email_html()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = RECIPIENT_ADDRESS_TEST;
    customers[1].format_opt = format_opt_html;
    
    CU_ASSERT_TRUE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_valid_email_invalid_format()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = RECIPIENT_ADDRESS_TEST;
    customers[1].format_opt = (format_opt_t)UINT_MAX;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_null_email_text()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = NULL;
    customers[1].format_opt = format_opt_text;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_null_email_html()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = NULL;
    customers[1].format_opt = format_opt_html;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_null_email_invalid_format()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = NULL;
    customers[1].format_opt = (format_opt_t)UINT_MAX;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_invalid_email_text()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = "1234567890";
    customers[1].format_opt = format_opt_text;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_invalid_email_html()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = "@nonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomain.com";
    customers[1].format_opt = format_opt_html;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_2_customers_1st_valid_2nd_invalid_email_invalid_format()
{
    customer_t customers[2];

    customers[0].email = RECIPIENT_ADDRESS_TEST;
    customers[0].format_opt = format_opt_text;

    customers[1].email = "1234567890@nonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomainnonexitentdomain.com";
    customers[1].format_opt = (format_opt_t)UINT_MAX;
    
    CU_ASSERT_FALSE(EMAIL_SendStatistics(2, customers));
}

void test_send_statistics_25_valid_customers()
{
    customer_t customers[25];
    int i;

    for (i = 0; i < 25; ++i)
    {
        customers[i].email = RECIPIENT_ADDRESS_TEST;
        customers[i].format_opt = (format_opt_t)(rand() % 2);
    }

    CU_ASSERT_TRUE(EMAIL_SendStatistics(25, customers));
}

void add_test_suite_email()
{
    CU_pSuite pSuite = NULL;

    /* add a suite to the registry */
    pSuite = CU_add_suite("Email test suite", init_test_suite_email, cleanup_test_suite_email);
    if (NULL == pSuite) {
        print_last_error();
        return;
    }

    /* add the tests to the suite */
    if ((NULL == CU_ADD_TEST(pSuite, test_email_send_without_attachments)) ||
        (NULL == CU_ADD_TEST(pSuite, test_email_receive_and_compare_without_attachments))
        
        /* keep the number of email tests small when using gmail since running 
           all tests would take considerable time */
#ifdef USE_LOCAL_EMAIL_SERVER
        ||
        (NULL == CU_ADD_TEST(pSuite, test_email_send_with_attachments)) ||
        (NULL == CU_ADD_TEST(pSuite, test_email_receive_and_compare_with_attachments)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_0_customers_null_customer)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_0_customers_valid_customer)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_valid_email_text)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_valid_email_html)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_valid_email_invalid_format)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_null_email_text)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_null_email_html)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_null_email_invalid_format)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_invalid_email_text)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_invalid_email_html)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_1_customer_invalid_email_invalid_format)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_valid_email_text)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_valid_email_html)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_valid_email_invalid_format)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_null_email_text)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_null_email_html)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_null_email_invalid_format)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_invalid_email_text)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_invalid_email_html)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_2_customers_1st_valid_2nd_invalid_email_invalid_format)) ||
        (NULL == CU_ADD_TEST(pSuite, test_send_statistics_25_valid_customers))
#endif
       )
    {
        print_last_error();
        return;
    }
}
